<script setup>
import {ref, computed} from 'vue'
import {INPUT_TYPE_TEXT, INPUT_TYPE_PASSWORD} from '@/constant/tail_input_constant.js'

/**
 * 接收父组件传递的参数
 */
const props = defineProps({
  // 输入框组件的背景颜色，用于需要特别指定输入框背景颜色的场景，不采用主题中的颜色
  bgColor: {type: String, default: 'var(--tail-input-bgc)'},
  // 输入框文字大小
  fontSize: {type: String, default: '1rem'},
  // 是否显示前缀图标
  enablePreIcon: {type: Boolean, default: true},
  // 是否显示清除图标
  enableClear: {type: Boolean, default: false},
  // 是否开启输入框聚焦边框
  enableIptFocusBorder: {type: Boolean, default: true},
  // 是否显示展示密码图标
  enableShowPassword: {type: Boolean, default: false},
  // 是否禁用输入框
  disable: {type: Boolean, default: false},
  // 接收父组件 v-model 绑定的数据
  modelValue: {default: ''},
  // 最大输入长度
  maxlength: {type: Number, default: -1},
  // 是否显示字数限制
  enableShowWordLimit: {type: Boolean, default: false},
  // 输入框的类型
  type: {type: String, default: INPUT_TYPE_TEXT},
  // 输入框的提示文字
  placeholder: {type: String, default: ''},
  // 输入框的 name 属性
  name: {type: String, default: ''},
  // 自动获取焦点
  autofocus: {type: Boolean, default: false},
  // 设置最小值
  min: {type: Number},
  // 设置最大值
  max: {type: Number},
  // 设置输入字段的合法数字间隔
  step: {type: Number, default: 1},
})

/**
 * 动态生成输入框组件样式
 */
const inputStyle = {
  backgroundColor: props?.disable ? 'var(--tail-input-disable-bgc)' : props?.bgColor,
  border: `${props?.disable ? 'var(--tail-input-disable-bgc)' : props?.bgColor} 1px solid`
}

/**
 * 动态生成输入框样式
 */
const iptStyle = {
  fontSize: props?.fontSize
}

/**
 * 小尾巴输入框组件聚焦与失焦处理
 */
// 小尾巴输入框组件的引用
const tailInputRef = ref(null)

// 小尾巴输入框组件聚焦事件处理器函数
function iptFocusHandler() {
  // 当开启输入框聚焦边框时，才执行聚焦时改变边框颜色
  if (props?.enableIptFocusBorder) {
    // 为组件设置边框
    tailInputRef.value.style.border = 'var(--tail-input-focus-border-color) 1px solid'
  }
  // 禁用输入框时的边框颜色
  if (props?.disable) {
    tailInputRef.value.style.border = 'var(--tail-input-disable-bgc) 1px solid'
  }
}

// 小尾巴输入框组件失焦事件处理器函数
function iptBlurHandler() {
  // 当开启输入框聚焦边框时，才执行失焦时取消为组件设置的边框
  if (props?.enableIptFocusBorder) {
    // 取消为组件设置的边框
    tailInputRef.value.style.border = 'var(--tail-input-border-color) 1px solid'
  }
  // 禁用输入框时的边框颜色
  if (props?.disable) {
    tailInputRef.value.style.border = 'var(--tail-input-disable-bgc) 1px solid'
  }
}

/**
 * 接收父组件绑定的自定义事件
 */
const emits = defineEmits([
  // 实现组件间 v-model 数据的双向绑定的事件
  'update:modelValue',
  'blur',
  'focus',
  'change',
  'input',
  'clear'
])

/**
 * 实现组件间 v-model 数据的双向绑定
 */
// 保存父组件传递过来的输入框的 value 数据
const value = ref(props?.modelValue)

// 触发自定义事件 update:modelValue 将输入框的 value 数据传递回去
function emitUpdateModelValue() {
  // 如果开启了限制输入框输入的最大输入长度，对输入进行截断
  if (props?.maxlength !== -1) {
    value.value = value.value?.slice(0, props?.maxlength)
  }
  emits('update:modelValue', value.value)
}

/**
 * 触发向组件绑定的输入框相关事件
 */
// 输入框失去焦点时触发
function emitBlur(evt) {
  emits('blur', value.value, evt)
}

// 输入框获得焦点时触发
function emitFocus(evt) {
  emits('focus', value.value, evt)
}

// 仅当 v-model value 改变时，当输入框失去焦点或用户按Enter时触发
function emitChange(evt) {
  emits('change', value.value, evt)
}

// 在 Input 值改变时触发
function emitInput(evt) {
  emits('input', value.value, evt)
}

// 在点击清空图标时触发
function emitClear(evt) {
  // 清空输入框
  value.value = ''
  // 将空输入传递给父组件
  emitUpdateModelValue()
  // 触发绑定在组件上的清除事件
  emits('clear', value.value, evt)
}

/**
 * 切换密码显示状态
 */
// 控制密码是否显示
const passwordIsShow = ref(false)
// 计算输入框类型，并添加切换密码显示状态功能
const inputType = computed(() => {
  // 只有开启切换密码显示状态，并且为密码框时，才可以切换
  if (props?.enableShowPassword && props?.type === INPUT_TYPE_PASSWORD) {
    return passwordIsShow.value ? INPUT_TYPE_TEXT : INPUT_TYPE_PASSWORD
  }
  // 如果没有开启切换密码显示状态，输入框类型为指定的类型
  return props?.type
})

</script>

<template>
  <!-- 小尾巴输入框组件容器 -->
  <div class="tail-input-container">
    <!-- 小尾巴输入框组件头部追加元素 -->
    <div class="tail-input-prepend">
      <slot name="prepend"></slot>
    </div>
    <!--
      小尾巴输入框组件
      在聚焦事件的捕获阶段执行处理器函数 iptFocusHandler
      在失焦事件的捕获阶段执行处理器函数 iptBlurHandler
    -->
    <div
        class="tail-input"
        :style="inputStyle"
        ref="tailInputRef"
        @focus.capture="iptFocusHandler"
        @blur.capture="iptBlurHandler"
        @input="emitUpdateModelValue"
    >
      <!-- 禁用输入框遮罩 -->
      <div v-show="disable" class="tail-input-disable-cover"></div>
      <!-- 输入框前的图标 -->
      <div v-if="enablePreIcon" class="tail-input-pre-icon">
        <slot name="preIcon"></slot>
      </div>
      <!-- 输入框 -->
      <input
          class="tail-input-ipt"
          :style="iptStyle"
          :type="inputType"
          :placeholder="placeholder"
          :name="name"
          :autofocus="autofocus"
          :min="min"
          :max="max"
          :step="step"
          v-model="value"
          @blur="emitBlur"
          @focus="emitFocus"
          @change="emitChange"
          @input="emitInput"
      />
      <!-- 显示字数限制 -->
      <div v-if="enableShowWordLimit" class="tail-input-show-word-limit">
        <span>{{ value?.length }}</span>
        <span>/</span>
        <span>{{ maxlength }}</span>
      </div>
      <!-- 展示密码图标 -->
      <div
          v-if="enableShowPassword"
          class="tail-input-show-pwd-icon"
          @click="passwordIsShow = !passwordIsShow"
      >
        <slot v-if="passwordIsShow" name="hidePwdIcon"></slot>
        <slot v-else name="showPwdIcon"></slot>
      </div>
      <!-- 清除图标 -->
      <div
          v-if="enableClear"
          class="tail-input-clear-icon"
          @click="emitClear"
      >
        <slot name="clearIcon"></slot>
      </div>
    </div>
    <!-- 小尾巴输入框组件尾部追加元素 -->
    <div class="tail-input-append">
      <slot name="append"></slot>
    </div>
  </div>
</template>

<style scoped lang="scss">
/*
 * 小尾巴输入框组件容器
 */
.tail-input-container {
  display: flex;
  justify-content: start;
  align-items: center;

  /*
   * 小尾巴输入框组件头部追加元素
   */
  .tail-input-prepend {
    height: 100%;
  }

  /*
   * 小尾巴输入框组件
   */
  .tail-input {
    position: relative;
    box-sizing: border-box;
    width: 100%;
    height: 100%;
    padding: 0 0.5rem;
    border-radius: 5px;
    border: var(--tail-input-border-color) 1px solid;
    background-color: var(--tail-input-bgc);
    transition: all 0.3s;
    display: flex;
    justify-content: start;
    align-items: center;
    color: var(--tail-input-font-color);

    /*
     * 清除元素默认样式
     */
    input, div, span {
      margin: 0;
      padding: 0;
      border: none;
      // 去除输入框选中时候的边框
      outline: none;
      background-color: transparent;
    }
    // 取消输入框自带的清除和显示密码
    input::-ms-clear,
    input::-ms-reveal {
      display: none;
    }

    /*
     * 禁用输入框遮罩
     */
    .tail-input-disable-cover {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      cursor: no-drop;
    }

    /*
     * 图标共同样式
     */
    .tail-input-pre-icon,
    .tail-input-clear-icon,
    .tail-input-show-pwd-icon {
      user-select: none;
      color: var(--tail-input-icon-color);
    }

    /*
     * 输入框前的前缀图标
     */
    .tail-input-pre-icon {
      padding-right: 0.5rem;
    }

    /*
     * 输入框
     */
    .tail-input-ipt {
      box-sizing: border-box;
      width: 100%;
      height: 100%;
      padding: 0.5rem 0;
      color: var(--tail-input-font-color);

      &::placeholder {
        color: var(--tail-input-placeholder-color);
      }
    }

    /*
     * 显示字数限制
     */
    .tail-input-show-word-limit {
      padding-left: 0.5rem;
      user-select: none;
      color: var(--tail-input-icon-color);
    }

    /*
     * 清除图标
     */
    .tail-input-clear-icon {
      padding-left: 0.5rem;
      cursor: pointer;
    }

    /*
     * 展示密码图标
     */
    .tail-input-show-pwd-icon {
      padding-left: 0.5rem;
      cursor: pointer;
    }
  }

  /*
   * 小尾巴输入框组件尾部追加元素
   */
  .tail-input-append {
    height: 100%;
  }
}

</style>